/***
 * @Description: 基于TCP协议的服务器端实现的C语言文件
 * @Author: Liverpool
 * @Date: 2024-01-14 22:12:33
 * @LastEditTime: 2024-01-14 22:12:40
 * @LastEditors: Liverpool
 */
#include "tcp_echo_server.h"

/***
 * @description: 基于TCP协议的echo的服务器端主函数
 * @Author: Liverpool
 * @Date: 2024-01-14 22:18:30
 * @return {*}
 * @param {int} port 需要连接的端口号
 */
void tcp_echo_server_start(int port)
{
    plat_printf("tcp server start, port = %d\n", port); // 打印需要连接的服务器端口

    WSADATA wsaData;                      // 需要进行相应的socket库绑定,使用Socket的程序在使用Socket之前必须调用WSAStartup函数
    WSAStartup(MAKEWORD(2, 2), &wsaData); // MAKEWORD是将两个byte型合并成一个word型，一个在高8位(b)，一个在低8位(a),使用WINSOCK2版本
    // 1.创建套接字
    SOCKET socket1 = socket(AF_INET, SOCK_STREAM, 0); // 使用IPv4协议 数据流的形式进行传输 0表示不使用协议
    if (socket1 == INVALID_SOCKET)                    // 获取的socket关键字不合法
    {
        plat_printf("tcp echo server:open socket error\n");
        goto end;
    }
    // 2.对需要连接的IP地址进行指定
    struct sockaddr_in server_addr; // 服务器端的IP地址
    plat_memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;         // 使用IPv4协议
    server_addr.sin_addr.s_addr = INADDR_ANY; // 可以连接任何IP地址
    server_addr.sin_port = htons(port);       // 再intel机器上用的是小端编码 而在网络上的用的是大端存储 所以需要进行小端到大端的转换

    // 3 与socket进行绑定
    if (bind(socket1, (const struct sockaddr *)&server_addr, sizeof(server_addr)) < 0)
    {
        // 如果连接不上就报错
        plat_printf("bind error\n");
        goto end;
    }
    // 4 侦听客户端的连接
    listen(socket1, 5);
    // 5 等待客户端的连接
    while (1)
    {
        // 6 客户端监听套接字的创建
        struct sockaddr_in client_addr;
        socklen_t addrlen = sizeof(client_addr); // 得到套接字字节数
        SOCKET client_socket = accept(socket1, (struct sockaddr *)&client_addr, (int *)&addrlen);
        if (client_socket == INVALID_SOCKET)
        {
            plat_printf("accept error\n");
            break;
        }
        // 打印出连接的客户端的IP地址
        plat_printf("tcp echo server:connect ip: %s ,port:%d\n",
                    inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port)); // inet_ntoa()把整形的IP地址变成字符串 ntohs()进行大端到小端的转化
        // 7 进行收发数据的处理
        char buffer[128];
        ssize_t size = 0;
        while ((size = recv(client_socket, buffer, sizeof(buffer), 0)) > 0)
        {
            plat_printf("recv size:%d\n", (int)size);
            send(client_socket, buffer, size, 0);
        }
        closesocket(client_socket);
    }
end:
    // 关闭套接字
    if (socket1 != INVALID_SOCKET)
    {
        closesocket(socket1); // 关闭套接字
    }
}